home *** CD-ROM | disk | FTP | other *** search
/ NetNews Offline 2 / NetNews Offline Volume 2.iso / news / comp / std / c++ / 541 < prev    next >
Encoding:
Text File  |  1996-08-06  |  22.9 KB  |  582 lines

  1. Path: engnews1.Eng.Sun.COM!taumet!clamage
  2. From: "Constantine Antonovich:" <const@Orbotech.Co.IL>
  3. Newsgroups: comp.std.c++
  4. Subject: operators new[]/delete[]
  5. Date: 26 Feb 1996 16:07:51 GMT
  6. Organization: Orbotech ltd.
  7. Approved: clamage@eng.sun.com (comp.std.c++)
  8. Message-ID: <313166CF.11C2@orbotech.co.il>
  9. NNTP-Posting-Host: taumet.eng.sun.com
  10. Mime-Version: 1.0
  11. Content-Type: text/plain; charset=us-ascii
  12. Content-Transfer-Encoding: 7bit
  13. X-Nntp-Posting-Host: orange.orbotech.co.il
  14. X-Mailer: Mozilla 2.0 (X11; I; SunOS 5.5 sun4c)
  15. Originator: clamage@taumet
  16.  
  17. Several days  ago I sent to this  newsgroup the following code
  18. (with a question is the  code  incorrect according to current ANSI
  19. standard or do I have a bug in my compiler):
  20.  
  21. //----------------------------------------------------------
  22. #include <iostream.h>
  23. #include <assert.h>
  24. #include <new.h>
  25.  
  26. class A {
  27. public:
  28.   A(void)     { cout << "A constructed" << endl; }
  29.   ~A()        { cout << "A destructed" << endl; }
  30. };
  31.  
  32. class B {
  33. public:
  34.   B(void)     { cout << "B constructed" << endl; }
  35.   ~B()        { cout << "B destructed" << endl; }
  36. };
  37.  
  38. A* foo_allocate(unsigned size)
  39. {
  40.   assert(sizeof(A)==sizeof(B));
  41.  
  42.   B* bptr=new B[size];
  43.   A* aptr=(A*)bptr; // A* aptr=reinterpret_cast<A*>bptr; is more correct
  44.                     // but my compilers do not support that yet.
  45.   for (unsigned j=size; j>0;) // this place corrected according
  46.     (bptr+(--j))->~B();       // to remark of a person whose name
  47.                               // I have lost to my regret.
  48.   for (unsigned i=0; i<size; ++i)
  49.     new(aptr+i) A;
  50.   
  51.   return aptr;
  52. }
  53.  
  54. int main(void)
  55. {
  56.   A* arr=foo_allocate(2);
  57.   delete [] arr;          # here
  58.   return 0;
  59. }
  60. //----------------------------------------------------------
  61.  
  62.     I  considered  as a problematic point the fact that one  of my
  63. compilers treats line marked "#here" as destruction  of array of B
  64. objects (B class destructors called).
  65.  
  66.     I  have  received several  answers   (I  send  this  code also
  67. personally to Steve Clamage  and he kindly answered me) concluding
  68. that according to  the standards, the program contains  operations
  69. with  undefined result and so   cannot be considered  as a correct
  70. one. Meanwhile after some time of reflections I am going to insist
  71. on the following:
  72.  
  73.     -- ANSI work papers (April, 1995)  leave interpretation of the
  74.        correctness of the above-mentioned code ambiguous;
  75.     -- If so, it's necessary  to define this matter more precisely
  76.        to eliminate differences of the interpretation by different
  77.        compiler vendors;
  78.     -- The  code can be interpreted as  representing an absolutely
  79.        correct and well-defined behavior.
  80.  
  81.     In the following text, I am going to prove this point of view.
  82.  
  83.  
  84.               1. Little history of the code sample.
  85.  
  86.     The above-mentioned code sample can be considered as a play of
  87. imagination without any applicable weight. Meanwhile this code was
  88. born from another one making little more sense.
  89.     Some times ago, I  noticed periodically  appearing discussions
  90. about necessity of renew operation in C++. Generally, I never felt
  91. myself out  of my share because  of renew nonexistence  but seeing
  92. arguments of  its usefulness again and  again, I started to think:
  93. what the hell is its problem, if it's not possible to implement it
  94. by means of the language itself?
  95.     So I wrote the  following  example, trying to  avoid redundant
  96. operations usually existing in reallocation on classic manner:
  97.  
  98. //----------------------------------------------------------
  99. template<class T>
  100. class Allocator {
  101. private:
  102.   struct filler { char filler_[sizeof(T)]; };
  103. public:
  104.   static T*   allocate_array(unsigned elm);
  105.   static void dup_array(T* dst, T* src, unsigned elm);
  106.   static void fill_array(T* dst, unsigned elm);
  107. };
  108.  
  109. template<class T>
  110. T* Allocator<T>::allocate_array(unsigned elm)
  111. {
  112.   return (T*)new filler[elm];
  113. }
  114.  
  115. template<class T>
  116. void Allocator<T>::dup_array(T* dst, T*src, unsigned elm)
  117. {
  118.   for (unsigned i=0; i<elm; ++i)
  119.     new(dst+i) T(src[i]);
  120. }
  121.  
  122. template<class T>
  123. void Allocator<T>::fill_array(T* dst, unsigned elm)
  124. {
  125.   for (unsigned i=0; i<elm; ++i)
  126.     new(dst+i) T;
  127. }
  128.  
  129. template<class T>
  130. T* realloc(T*& array, unsigned old_size, unsigned new_size)
  131. {
  132.   set_new_handler(0);
  133.   T* tmp=Allocator<T>::allocate_array(new_size);
  134.   if (tmp) {
  135.     if (new_size>old_size) {
  136.       Allocator<T>::dup_array(tmp,array,old_size);
  137.       Allocator<T>::fill_array(rmp+old_size,new_size-old_size);
  138.     }
  139.     else
  140.       Allocator<T>::dup_array(tmp,array,new_size);
  141.     delete [] array; array=tmp;
  142.   }
  143.   return tmp;
  144. }
  145. //----------------------------------------------------------
  146.  
  147.     The   general  idea  was to     allocate  an  array with    no
  148. initialization of its objects and just  to use copy constructor to
  149. copy old    elements into  the newly    allocated  array (avoiding
  150. redundant creation of  objects,  with their default   constructor,
  151. when they are immediately  replaced by the following  assignment). 
  152. There is no problems of such  technique usage in container classes
  153. where all  memory and object management  are absolutely hided from
  154. the user, but the  imaginary renew operation should  be applicable
  155. to regular arrays like:
  156.  
  157. //----------------------------------------------------------
  158.     A* ap=new A[4];
  159.     realloc(ap,4,8);
  160.     delete [] ap;
  161. //----------------------------------------------------------
  162.  
  163.     Obviously     the    code     should     be    contained    in
  164. Allocator<T>::allocate_array   function  produces  the   mentioned
  165. problem.
  166.  
  167.               2. Undefined behavior.
  168.  
  169.     Of  course, some  constructions  in   a possible program   may
  170. produce undefined behavior.  Nevertheless, even undefined behavior
  171. should have some definition. Let's consider the following code:
  172.  
  173. //----------------------------------------------------------
  174.       A* ap=new A;
  175.       B* bp=reinterpret_cast<B*>(ap);
  176.       delete bp; // #here
  177. //----------------------------------------------------------
  178.  
  179. Without any doubt, result of the code  executed in "#here" line is
  180. undefined.   But  definitely  I  wouldn't   like,  as a result  of
  181. uncertainty of the behavior, my  compiler to send email  complaint
  182. to  some   League "C++  compilers     against  stupidity of    the
  183. programmers".  Also   I  wouldn't like   my  compiler to recognize
  184. incorrectness of the code and silently to call A destructor (after
  185. all, bp  points to A  object, isn't it?) instead of  B one. Here I
  186. mean that
  187.  
  188.          UNDEFINED BEHAVIOR HAS AN ERROR MEANING.
  189.    UNDEFINED BEHAVIOR ALWAYS RESULTING IN CORRECT EXECUTION OF
  190.        A CODE WITH UNDEFINED BEHAVIOR, IS FORBIDDEN.
  191.  
  192.     In  the   previous example,   we are dealing with a code  with
  193. undefined behavior. The code is obviously incorrect. Meanwhile, in
  194. case of an imaginary compiler which can recognize  true type of an
  195. object,  the   compiler could call  A   destructor  in line marked
  196. "#here"  (because  its behavior whould be   undefined anyway).  In
  197. such a case, the  invalid code will  be correctly executes in  any
  198. case (ALWAYS) and   so the  behavior  of  the compiler  cannot  be
  199. considered as a proper one.
  200.     C++,  partly by itself,   partly as heir  of  C, stands on the
  201. principles  of not stinting of a  programmer in correctness of his
  202. actions  if they don't contradict  syntactical correctness.    So,
  203. in the example, in the line marked "#here", the imaginary compiler
  204. should    honestly try to  destroy  B    object  and  to free  its
  205. memory. Applying  of B destructor to A  object most  probably will
  206. cause "undefined behavior", but its harm will depend on particular
  207. A and B classes (and obviously such applying will be not harmless
  208. ALWAYS).
  209.  
  210.  
  211.               3. No kidding.
  212.  
  213.     C++   is not wizard   language.    Generally, its  behavior is
  214. understandable, enough clear and well predictable. Creation of C++
  215. objects consists  of  two  parts:  memory allocation   and  object
  216. construction  by itself.   Even   if  such  separation   into  two
  217. independent parts  is not  obvious and  is not  proclaimed by ANSI
  218. draft straightly, that    doesn't  change anything   because  such
  219. separation results from the language definition anyway.
  220.     C++ memory management is ALMOST  ALWAYS typeless. Here "ALMOST
  221. ALWAYS" stands for   all   cases covered  by   standard-conforming
  222. language   implementation except of   denumerable  number of cases
  223. where  a programmer  explicitly changes the   language behavior by
  224. means of the language constructions (and, I should add, on his own
  225. responsibility). To illustrate the term, the following example can
  226. be considered:
  227.  
  228. //----------------------------------------------------------
  229.     A* ap=new A;
  230. //----------------------------------------------------------
  231.  
  232. What does the code do? Obviously, it creates  a new object of type
  233. A.  Yes, but I should say it creates a new object of type A ALMOST
  234. ALWAYS, just because the definition of A class may be as follows:
  235.  
  236. //----------------------------------------------------------
  237.     class A {
  238.       // some stuff
  239.     public:
  240.       // some stuff
  241.       void* operator new(unsigned) { exit(1); } // not for heap usage
  242.     };
  243. //----------------------------------------------------------
  244.  
  245. and in such case obviously the  previous statement will not create
  246. any A object.
  247.     C++ memory    management  is ALMOST   ALWAYS  typeless because
  248. default operators new  and new[] has   no knowledge about type  of
  249. object they  allocate  memory for.  From   the other  hand,  these
  250. operators     are  the  single   C++   mechanism    to manage  the
  251. memory. Moreover,  this and only this part  of object creation can
  252. be   absolutely overloaded by   a  programmer and that  absolutely
  253. separates it to independent stage of object creation.
  254.     An   object    construction  has    hidden    features   (like
  255. initialization   of tables of virtual   functions) and only partly
  256. (constructors and destructors) can  be influenced by a programmer. 
  257. Meanwhile, declaration in the ANSI  standard "placement new"  also
  258. had  finished separation of   object construction into independent
  259. part since  an object can be created  with no allocation of memory
  260. (at  any place and  by the programmer, not  only on the stack) and
  261. can be legally destroyed with no freeing of  the memory (by direct
  262. call to its destructor).
  263.     If we recall  that according to C++ principles there should be
  264. no difference between objects  and their behavior regardless their
  265. placement we have to agree that allocation of memory for an object
  266. and construction of the  object in the allocated memory  represent
  267. two independent stages ALMOST ALWAYS.
  268.     Taking all this into account, even definitions of operator new
  269. and delete can be reconsidered  to eliminate number of  duplicated
  270. definitions, for example:
  271.  
  272.                         new T(<arg-list>);
  273.                  represents shorthand of sequence
  274.            new(::operator new(sizeof T)) T(<arg-list>);
  275.                                 or
  276.            new(T::operator new(sizeof T)) T(<arg-list>);
  277.                   if T::operator new is defined.
  278.  
  279.   delete tp; // there tp is non-null pointer on object of type T
  280.               represents shorthand of atomic sequence
  281. if (tp) { tp->~T(); ::operator delete(<cast-to-mostly-base>tp); }
  282.                                 or
  283. if (tp) { tp->~T(); T::operator delete(<cast-to-mostly-base>tp); }
  284.                 if T::operator delete is defined.
  285.  
  286.     Actually, similar redefinition can  be done for new[]/delete[]
  287. also.
  288.  
  289.            4. Alignment and memory allocation.
  290.  
  291.     In the starting the article example, the following code
  292.  
  293. //----------------------------------------------------------
  294.     assert(sizeof(A)==sizeof(B));
  295.  
  296.     B* bptr=new B[size];
  297.     A* aptr=(A*)bptr;
  298. //----------------------------------------------------------
  299.  
  300. really seems dangerous.
  301.  
  302.     Fergus Henderson writes:
  303.        "This assertion is not guaranteed to succeed.
  304.         It would take an extremely perverse implementation
  305.         for it to fail, however, so I think it would be very
  306.         portable, even though it is not strictly guaranteed
  307.         to work."
  308.  
  309. This sentence seems to be  reasonable but, in deal, this assertion
  310. guarantees the  correctness     ALMOST ALWAYS  and    under   that
  311. circumstance this check is absolutely portable.
  312.  
  313.     ANSI draft says:
  314.       18.4.1.1  Single-object forms
  315.       Effects:
  316.         The allocation function called by a new-expression to
  317.         allocate size  bytes  of storage  suitably aligned to
  318.         represent any object of that size.
  319.  
  320.       18.4.1.2  Array forms
  321.       Effects:
  322.         The  allocation  function called by the array form of
  323.         a new-expression to  allocate  size bytes  of  storage
  324.         suitably aligned to represent any array object of that
  325.         size or smaller.32)
  326.  
  327.     We see that ANSI draft   says that allocated memory should  be
  328. suitably  aligned  for any object  and  any array  object with the
  329. single  limitation  of   size.  We can  also   recall C++  pointer
  330. arithmetic   and what is sizeof  of  some particular object (which
  331. contains concept of alignment in the object itself).
  332.     An  implementation    hasn't  to be    extremely perverse  the
  333. assertion condition  to fail. It  can  be very simple  one where B
  334. class for example has its own operator  new[] allocating memory in
  335. specific alignment suitable for B but  not for any other class and
  336. A  one  particularly (and  even  that  is impossible for compilers
  337. still not supporting overloading of operator new[]).
  338.     But  this situation is exactly  "ALMOST  ALWAYS" case. If I am
  339. taking   responsibility to  overload  operator  new[] for specific
  340. class, it's also my responsibility to take a care of usage of such
  341. operations like one I am doing  with the condition of the equation
  342. of object sizes.
  343.  
  344.            The language  should  stand  in "ALMOST ALWAYS"
  345.         correctness (and de facto it does that).      If a
  346.         programmer  is  doing  something   that  is ALMOST
  347.         ALWAYS correct,  the  language should  demonstrate
  348.         behavior like that is ALWAYS correct. Since ALMOST
  349.         ALWAYS correct action may became incorrect only as
  350.         a result of  a programmer  activity,  this is also
  351.         responsibility of the programmer to take a care of
  352.         usage of such actions.
  353.  
  354.  
  355.     Fergus Henderson continues:
  356. //----------------------------------------------------------
  357.     B* bptr=new B[size];
  358.     A* aptr=(A*)bptr;
  359. //----------------------------------------------------------
  360.         "This cast has unspecified behavior. (See 5.2.9
  361.          [expr.cast.reinterpret]/8.). However, I would
  362.          expect it to work on most implementations."
  363.  
  364.     This  article    of ANSI draft    interprets the  operation as
  365. unspecified in case of cast from T1 to T2 and back and if there is
  366. difference in alignment of T1 and  T2.  Obviously, that is not our
  367. case (at least because definition of allocation function returning
  368. suitable for any object alignment).
  369.  
  370.  
  371.  
  372.             5. Rest in peace.
  373.  
  374.     The following  peace of the code has  been considered as clear
  375. by all experts:
  376.  
  377. //----------------------------------------------------------
  378.   for (unsigned j=size; j>0;)
  379.     (bptr+(--j))->~B();
  380.  
  381.   for (unsigned i=0; i<size; ++i)
  382.     new(aptr+i) A;
  383. //----------------------------------------------------------
  384.  
  385.  
  386.         6. Undefined behavior (continue).
  387.  
  388.     All experts have considered deletion of the array allocated in
  389. so  strange manner as    mostly  incorrect point with    undefined
  390. behavior.
  391.  
  392.     Steve Clamage writes:
  393.         "You do have an operation  with undefined results,
  394.         however. In effect you are doing this:
  395.         A* p = (A*)new B[2];
  396.         delete [] p;
  397.         The rule  is  that  the type of the pointer passed
  398.         to delete[]  must  match  the type  of the pointer
  399.         returned  by  new[],  which is  not  the case here.
  400.         The compiler is not required to diagnose the error, 
  401.         and the language  definition does  not say what the
  402.         result is."
  403.  
  404.     Definitely,  I am not   doing that.  If   the standard of  the
  405. language enables to interpret my code in such manner then there is
  406. something  wrong with  the standard! But  even if  the behavior is
  407. proclaimed to be  undefined, I would like to   remind what I  have
  408. said in  2-nd paragraph. If  the  uncertainty of the  behavior was
  409. properly defined then  the code wouldn't  have undefined behavior! 
  410. (It would  be very interesting  to  test the code  on some another
  411. compilers.    I may suppose   that the code  has,  in deal, enough
  412. defined behavior de facto as result of most logical implementation
  413. of  operators new/delete and  just  SPARCompiler C++, for  unknown
  414. reason, stores pointer  to destructor function together with array
  415. size).
  416.  
  417.     Fergus Henderson agrees with Steve Clamage:
  418.         "This has undefined behaviour.      It contravenes 5.3.5
  419.          [expr.delete]/2,   which   says  that  the   expression
  420.          passed  to `delete []'  must be  a pointer to the first
  421.          element of an array of objects allocated with `new []';
  422.          this is not the case,  because  although there once was
  423.          such an  array at  that memory location,   its lifetime
  424.          ended  when the memory  was  overwritten  by  the calls
  425.          to placement new (see 3.8[basic.life]/1)."
  426.  
  427.     There  is    at least one   self-contradictory  point  in that
  428. conclusion.  Of course, lifetime of all B  objects had been ended,
  429. by why does that  mean end of the  array life? Or in contrary,  if
  430. end of life-time of B objects means end of life-time of the array,
  431. so  creation  of  A objects  should  mean  creation of new  array,
  432. shouldn't it?.
  433.  
  434.     Article 5.3.5.2 of ANSI draft says something slightly
  435.     different:
  436.         "...In the second  alternative  (delete array), the value
  437.         of  the  operand  of  delete  shall  be  a pointer  to an
  438.         array created by a new-expression without a new-placement
  439.         specification. If not, the behavior is undefined."
  440.  
  441.     So delete takes as its argument POINTER TO ARRAY (even objects
  442. are not mentioned). No one says that 
  443.         ...pointer passed to delete[] must match the type
  444.         of the pointer returned  by  new[]...
  445.         ...the expression passed to `delete []' must be a
  446.         pointer to the first element of an array of objects
  447.         allocated with `new []'...
  448. All that already  means  INTERPRETATION of  the standard and  also
  449. that the  standard   enables such interpretations.  Meanwhile  C++
  450. memory  management seems not to  need  so strong restrictions just
  451. because   the  memory  can  be   managed  separately from  objects
  452. construction/destruction and can  be reused without reallocation.  
  453. I  agree  that all  above said  regarding  operators new/delete is
  454. point of view of common sense (one should  use delete and delete[]
  455. with the same  pointer to the same type  he got from new and new[]
  456. and  not   play with  the    pointers in  99.9999% cases  he  uses
  457. new/delete at all and in 100% cases if  he doesn't understand what
  458. he is doing) but that has nothing common with boundaries of proper
  459. language processing.
  460.     And here we really arrive to the final point. ANSI draft gives
  461. no   strong   array   definition   to   disable  ambiguous   array
  462. interpretation.  And    above-mentioned  common-sense based  array
  463. understanding has all rights to exist.
  464.  
  465.             7. No kidding (continue).
  466.  
  467.     I suppose that this  ambiguity in interpretation of arrays and
  468. operators new/delete  should be  eliminated from the  standard.  I
  469. would propose the following additions in supposition that they:
  470.  
  471.       -- do   not  conflict  with  any    of   previous standard's
  472.          definitions;
  473.       -- do not change nothing in the standard's common principles
  474.          and  common understanding of  the standard except of very
  475.          specific point with no influence upon the standard itself;
  476.       -- do not   influence mostly on existing implementations  of
  477.          the language since some implementations  use this idea de
  478.          facto and others can easily be corrected;
  479.       -- do  not   influence mostly on  existing  C++ applications
  480.          because  they  concern some  very  specific  point in the
  481.          standard  with not  common and extremely  rarely (if any)
  482.          usage.
  483.       -- will make the standard more logically completed.
  484.  
  485.     Addition to array definition [dcl.array]:
  486.         Array of N T object represents contiguous amount of
  487.         memory  of  suitable  size  and  alignment  with  N
  488.         non-overlapping objects  of type T  placed into the
  489.         memory with no gaps and each properly aligned.
  490.  
  491.     Addition to operator delete [expr.delete] ("above" here
  492.     means all previously said by the standard):
  493.         In either alternative, the type of the deleted object
  494.         is evaluated  as described above and according to the
  495.         type of the actual operand.
  496.  
  497.     In  my    opinion,  the additions  may    be  considered as an
  498. overweight but the experience shows they are not.
  499.  
  500.                  8. Renew
  501.  
  502.     In my opinion, the previously mentioned additions   would make
  503. starting the article  example  absolutely clear and  ALMOST ALWAYS
  504. correct with no discussions (I continue to insist that the example
  505. is so even now but  with discussions). But  what about renew?  C++
  506. standard  very hardly accepts new  keywords and new features.  But
  507. may be  it makes sense, at  least for completeness,  to add to the
  508. standard's (enough)  new family of various_cast<T>  something like
  509. following:
  510.  
  511. dynamic_sizeof(object); // should return sizeof evaluated in
  512.                         // run time, for example:
  513.  
  514. //----------------------------------------------------------
  515. class A {
  516. public:
  517.   unsigned u;
  518.   A();
  519.   virtual ~A();
  520. };
  521.  
  522. class B : public A {
  523. public:
  524.   int i;
  525.   B();
  526.   ~B();
  527. };
  528.  
  529. int main(void)
  530. {
  531.   A* a=new B;
  532.   cout << sizeof(A)          << endl; // types 8
  533.   cout << sizeof(B)          << endl; // types 12
  534.   cout << sizeof(*a)         << endl; // types 8
  535.   cout << dynamic_sizeof(*a) << endl; // types 12
  536.   return 0;
  537. }
  538. //----------------------------------------------------------
  539.  
  540.  
  541. array_sizeof(pointer); // should return sizeof of an array, for example
  542.  
  543. //----------------------------------------------------------
  544. class A {
  545. public:
  546.   unsigned u;
  547.   A();
  548. };
  549.  
  550. int main(void)
  551. {
  552.   A* a=new A;
  553.   A  ar[2];
  554.   A* ap=new A[4];
  555.  
  556.   cout << array_sizeof(a)/sizeof(A)  << endl; // types 0
  557.   cout << array_sizeof(ar)/sizeof(A) << endl; // types 2
  558.   cout << array_sizeof(ap)/sizeof(A) << endl; // types 4
  559.   return 0;
  560. }
  561. //----------------------------------------------------------
  562.  
  563.     I   may suppose   that usage of    arrays of  objects with  no
  564. destructors (where a compiler may  optimize away storage of number
  565. of elements) became enough rare in contemporary C++ (and in lot of
  566. cases, number  of elements in   such arrays  is known already   at
  567. compilation  time) so  the  language may  enough easily  provide a
  568. programmer  with  such information  as  number of  elements in  an
  569. array.
  570.  
  571. -- 
  572. //------------------------------------------------------------------
  573. // Opinions expressed here are my own only
  574. // Constantine Antonovich                       const@orbotech.co.il
  575. //------------------------------------------------------------------
  576. [ To submit articles: Try just posting with your newsreader.
  577.               If that fails, use mailto:std-c++@ncar.ucar.edu
  578.   FAQ:    http://reality.sgi.com/employees/austern_mti/std-c++/faq.html
  579.   Policy: http://reality.sgi.com/employees/austern_mti/std-c++/policy.html
  580.   Comments? mailto:std-c++-request@ncar.ucar.edu
  581. ]
  582.